package basicknowledge;

import basicknowledge.java8.Album;
import basicknowledge.java8.Artist;
import basicknowledge.java8.Track;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Arrays.asList;

/**
 * Created by Zonda on 2017/1/18. java8学习题
 */
public class Java8Study {

/*    2. ThreadLocal Lambda 表达式。Java 有一个 ThreadLocal 类，作为容器保存了当前线程里
    局部变量的值。Java 8 为该类新加了一个工厂方法，接受一个 Lambda 表达式，并产生
    一个新的 ThreadLocal 对象，而不用使用继承，语法上更加简洁。
    a. 在 Javadoc 或集成开发环境（IDE）里找出该方法。
    b. DateFormatter 类是非线程安全的。使用构造函数创建一个线程安全的 DateFormatter
    对象，并输出日期，如“01-Jan-1970”。*/
//Supplier<T,R>
//    DateFormat safeDateFormat = ThreadLocal.withInitial(() -> new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy"))).get();

//    interface IntPred {
//        boolean test(Integer value);
//    }
//
//    boolean check(Predicate<Integer> predicate);
//
//    boolean check(IntPred predicate);

    ArrayList<Integer> list = new ArrayList<>();

    /*1. 常用流操作。实现如下函数：
    a. 编 写 一 个 求 和 函 数， 计 算 流 中 所 有 数 之 和。 例 如，int addUp(Stream<Integer>numbers)；
    */
    static int addUp(Stream<Integer> streams) {

//        streams.reduce(0, new BinaryOperator<Integer>() {
//            @Override
//            public Integer apply(Integer acc, Integer element) {
//                return acc + element;
//            }
//        });

        return streams.reduce(0, (Integer acc, Integer element) -> acc + element);
    }

    /*b. 编写一个函数，接受艺术家列表作为参数，返回一个字符串列表，其中包含艺术家的
    姓名和国籍；*/
    static List<String> getArtistInfo(List<Artist> artists) {
        return artists.stream()
                //答案里另一种写法：.flatMap(artist -> Stream.of(artist.getName(), artist.getNationality()))
                //.map(artist -> artist.getName() + artist.getNationality())
                .flatMap(artist -> Stream.of(artist.getName(), artist.getNationality()))
                .collect(Collectors.toList());
    }

    /*
     c. 编写一个函数，接受专辑列表作为参数，返回一个由最多包含 3 首歌曲的专辑组成的
     列表。*/
    static List<Album> getFilterAlbumList(List<Album> albumList) {
        return albumList.stream()
                .filter(album -> album.getTrackList().size() < 4)
                .collect(Collectors.toList());
    }

    /*2. 迭代。修改如下代码，将外部迭代转换成内部迭代：
    int totalMembers = 0;
    for (Artist artist : artists) {
        Stream<Artist> members = artist.getMembers();
        totalMembers += members.count();
    }*/
    static int getTotalMemberCount(List<Artist> artistList) {
        return (int) artistList.stream()
//                .flatMap(artist -> artist.getMembers())
                .flatMap(Artist::getMembers)
                .count();
    }

    /*    6. 计算一个字符串中小写字母的个数（提示：参阅 String 对象的 chars 方法）。
                */
    public static int countLowercaseLetters(String string) {
        return (int) string.chars()
                .filter(Character::isLowerCase)
                .count();
    }

    /* 7. 在一个字符串列表中，找出包含最多小写字母的字符串。对于空列表，返回 Optional
     <String> 对象。*/
    public static Optional<String> mostLowercaseString(List<String> strings) {
        return strings.stream()
                .max(Comparator.comparing(Java8Study::countLowercaseLetters));
    }
/*    1. 只用 reduce 和 Lambda 表达式写出实现 Stream 上的 map 操作的代码，如果不想返回
    Stream，可以返回一个 List。*/

    public static <I, O> List<O> map(Stream<I> stream, Function<I, O> mapper) {
        return stream.reduce(new ArrayList<>(),
                (List<O> acc, I x) -> {
                    // We are copying data from acc to new list instance. It is very inefficient,
                    // but contract of Stream.reduce method requires that accumulator function does
                    // not mutate its arguments.
                    // Stream.collect method could be used to implement more efficient mutable reduction,
                    // but this exercise asks to use reduce method.
                    List<O> newList = new ArrayList<>(acc);
                    newList.add(mapper.apply(x));
                    return newList;
                },
                (List<O> left, List<O> right) -> {
                    // We are copying left to new list to avoid mutating it.
                    List<O> newLeft = new ArrayList<>(left);
                    newLeft.addAll(right);
                    return newLeft;
                });
    }

/*            2. 只用 reduce 和 Lambda 表达式写出实现 Stream 上的 filter 操作的代码，如果不想返回
    Stream，可以返回一个 List。*/

    public static <T> List<T> filter(Stream<T> stream, Predicate<T> predicate) {

        return stream.reduce(
                //initial
                new ArrayList<>(),
                //accumulator -> 函数接口：BiFunction
                (List<T> acc, T x) -> {
                    if (!predicate.test(x)) {
                        return acc;
                    }
                    // We are copying data from acc to new list instance. It is very inefficient,
                    // but contract of Stream.reduce method requires that accumulator function does
                    // not mutate its arguments.
                    // Stream.collect method could be used to implement more efficient mutable reduction,
                    // but this exercise asks to use reduce method explicitly.
                    List<T> newList = new ArrayList<>(acc);
                    newList.add(x);
                    return newList;
                },
                //combiner -> 函数接口：BinaryOperator
                (List<T> left, List<T> right) -> {
                    // We are copying left to new list to avoid mutating it.
                    List<T> newLeft = new ArrayList<>(left);
                    newLeft.addAll(right);
                    return newLeft;
                });
    }

    public static void main(String[] args) {
        System.out.println("addUp: " + addUp(Stream.of(1, 2, 3)));

        System.out.println("getFilterAlbumList ----------------");
        List<Album> filterAlbumList = getFilterAlbumList(albumList);
        filterAlbumList.stream().forEach(album -> System.out.println(album.getName()));

        System.out.println("getArtistInfo ----------------");
        List<String> artistInfoList = getArtistInfo(membersOfTheBeatles);
//        artistInfoList.stream().forEach(info -> System.out.println(info));
        artistInfoList.stream().forEach(System.out::println);
    }

    public static final Artist johnColtrane = new Artist("John Coltrane", "US");

    public static final Artist johnLennon = new Artist("John Lennon", "UK");
    public static final Artist paulMcCartney = new Artist("Paul McCartney", "UK");
    public static final Artist georgeHarrison = new Artist("George Harrison", "UK");
    public static final Artist ringoStarr = new Artist("Ringo Starr", "UK");

    public static final List<Artist> membersOfTheBeatles = asList(johnLennon, paulMcCartney, georgeHarrison, ringoStarr);

    public static final Artist theBeatles = new Artist("The Beatles", membersOfTheBeatles, "UK");

    public static final Album aLoveSupreme = new Album("A Love Supreme",
            asList(new Track("Acknowledgement", 467), new Track("Resolution", 442)),
            asList(johnColtrane));

    public static final Album sampleShortAlbum = new Album("sample Short Album",
            asList(new Track("short track", 30)),
            asList(johnColtrane));

    public static final Album manyTrackAlbum = new Album("sample Short Album",
            asList(new Track("short track", 30), new Track("short track 2", 30),
                    new Track("short track 3", 30), new Track("short track 4", 30),
                    new Track("short track 5", 30)),
            asList(johnColtrane));

    static List<Album> albumList = asList(aLoveSupreme, sampleShortAlbum, manyTrackAlbum);
}
